#include "Team.h"
#include "Utilities.h"
#include "PlayerManager.h"
#include "ServiceProvider.h"
#include "ZStream.h"
#include "Player.h"
#include "NameManager.h"
#include "LeagueManager.h"

#include <algorithm>
#include <cassert>

TeamParams Team::s_params;

#pragma warning(push)
#pragma warning(disable: 4355)

Team::Team(ServiceProvider* const i_sp, const std::string& i_name, const size_t i_id) 
	:m_name(i_name)
	,m_userControlled(false)
	,m_budget(0)
	,m_id(i_id)
	,m_sp(i_sp)
	,m_ai(i_sp, this)
{
	assert(i_sp);
	m_teamHistory.m_totalPodium = 0;
	m_teamHistory.m_podium.resize(3,0);
	m_teamHistory.m_yearsInLeagueLayer.resize( m_sp->GetLeagueMgr().GetNumOfLayers(false) ,0);
}

#pragma warning(pop)

bool Team::AddPlayer(Player* i_player)
{
	if (m_players.size() < s_params.MAX_TEAM_PLAYERS)
	{	
		m_players.push_back(i_player);
		i_player->SetTeam(this);
		return true;
	}
	return false;
}

bool Team::RemovePlayer(Player* i_player)
{
	if (m_players.size() <= s_params.MIN_TEAM_PLAYERS)
		return false;
	
	PlayerVecIt it = std::find(m_players.begin(), m_players.end(), i_player);

	if(it == m_players.end())
		return false;

	(*it)->SetTeam(0);
	m_players.erase(it);
	return true;
}

bool Team::BuyPlayer(Player* i_player, const size_t price)
{
	assert(i_player);

	// check if the team can buy the player
	if(m_budget <= static_cast<int>(price))
		return false;

	const bool result = AddPlayer(i_player);

	if(result)
	{
		// update the budget
		m_budget -= price;
		m_sp->GetPlayerMgr().IngagePlayer(i_player);
	}

	return result;
}

bool Team::SellPlayer(Player* i_player)
{
	assert(i_player);

	const int value = i_player->SellValue();
	const bool result = RemovePlayer(i_player);

	if(result)
	{
		// update the budget
		m_budget += value;
		m_sp->GetPlayerMgr().SellPlayer(i_player);
	}

	return result;
}

void Team::SwapPlayers(int i_first, int i_second)
{
	std::swap(m_players.at(i_first), m_players.at(i_second));
}

int Team::GetTeamPoints(const int i_numYears) const
{
	const size_t scoreSize = m_teamPoints.size();
	size_t numYears = (i_numYears <= 0 ? scoreSize : i_numYears);
	numYears = std::min(numYears, scoreSize);

	int sum = 0;
	for(size_t i= scoreSize-numYears; i< scoreSize; ++i)
		sum += m_teamPoints[i];

	return sum;
}

void Team::AddTeamPoints(const int i_points)
{
	m_teamPoints.push_back(i_points);
}

void Team::ClearTeamPoints()
{
	m_teamPoints.clear();
}

size_t Team::CalculateTotalSalary() const
{
	size_t totalSalary = 0;
	for(PlayerVecCit it = m_players.begin(); it != m_players.end(); ++it)
		totalSalary += (*it)->GetSalary();
	return totalSalary;
}

void Team::EndTurnUpdate()
{
	// update budget
	m_budget -= CalculateTotalSalary();
}

int	Team::GetTeamPower() const
{
	int power = 0;
	for (PlayerVecCit It = m_players.begin(); It != m_players.end(); ++It)
		power += static_cast<int>((*It)->GetAverageSkill() * 100.0f);
	return power;
}

int Team::GetRoleByIndex(size_t i_index)
{
	if(i_index < s_params.TITOLARS_GOALKEEPERS)
		return Goalkeeper;
	
	i_index -= s_params.TITOLARS_GOALKEEPERS;
	if(i_index < s_params.TITOLARS_DEFENDERS)
		return Defender;

	i_index -= s_params.TITOLARS_DEFENDERS;
	if(i_index < s_params.TITOLARS_ATTACKERS)
		return Attacker;

	return NUM_ROLES;
}

int	Team::GetTitolarsByRole(const int i_role)
{
	switch (i_role)
	{
	case Goalkeeper:
		return s_params.TITOLARS_GOALKEEPERS;
	case Defender:
		return s_params.TITOLARS_DEFENDERS;
	case Attacker:
		return s_params.TITOLARS_ATTACKERS;
	}

	assert(0);
	return 	0;
}

ZStream& Team::Serialize(ZStream& o_stream)
{
	o_stream.WriteOpenTag("Team");
	o_stream.WriteAttribute("ID",			m_id);
	o_stream.WriteAttribute("Name",			m_name);
	o_stream.WriteAttribute("Budget",		m_budget);
	o_stream.WriteAttribute("PlayersNum",	m_players.size());
	
	PlayerVecCit end = m_players.end();
	PlayerVecCit it = m_players.begin();

	for(; it != end; ++it)
	{
		(*it)->Serialize(o_stream);
	}

	o_stream.WriteCloseTag("Team");
	
	return o_stream;
}

ZStream& Team::Deserialize(ZStream& i_stream)
{
	i_stream.SkipLine();
	i_stream.ReadAttribute(m_id);
	i_stream.ReadAttribute(m_name);
	i_stream.ReadAttribute(m_budget);

	size_t playersNum;

	i_stream.ReadAttribute(playersNum);

	for(size_t i = 0; i < playersNum; ++i)
	{
		Player& p = *m_sp->GetPlayerMgr().CreateNewPlayer(true,true);
		p.Deserialize(i_stream);
		p.SetTeam(this);

		m_players.push_back(&p);
	}

	m_sp->GetNameMgr().SetTeamNameUsed(m_name);

	i_stream.SkipLine();

	return i_stream;
}

